#include "rclcpp/rclcpp.hpp"
#include <chrono>
#include <ctime>
#include "chapt4_interfaces/srv/patrol.hpp"   //如果不编译，没有install 下的 chapt4_interfaces/srv/patrol.hpp，那么这里就会报错。
#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"

using SetP = rcl_interfaces::srv::SetParameters;
using Patrol = chapt4_interfaces::srv::Patrol;
using namespace std::chrono_literals; //可以使用 10ms 1000ms 这样来表示时间

class PatrolClient : public rclcpp::Node
{
private:
  rclcpp::Client<Patrol>::SharedPtr patrol_client_;
  rclcpp::TimerBase::SharedPtr timer_;
public:
  explicit PatrolClient(const std::string& node_name) : Node(node_name)
  {
    srand(time(nullptr)); //初始化随机数种子
    patrol_client_ = this->create_client<Patrol>("patrol");
    timer_ = this->create_wall_timer(
      10s,
      [&](void)->void{
        //1.检测服务端是否上线
        //如果服务没有上线 就返回false 否这就返回true，也就是说 只要service没有上线，程序就会在这里阻塞住
        while (this->patrol_client_->wait_for_service(1s) == false)  
        {
            if(rclcpp::ok() == false){
                RCLCPP_ERROR(this->get_logger(),"在等待服务上线的时候 rclcpp挂了，程序退出");
                return;
            }
            RCLCPP_ERROR(this->get_logger(),"等待服务上线中...");
        }
        RCLCPP_ERROR(this->get_logger(),"服务连接成功...");
        //2.构造请求的对象
        auto request = std::make_shared<Patrol::Request>();
        request->target_x = rand()%15; //%15, 对这个随机整数进行处理，将其结果限制在 0 到 14 之间（包括 0 和 14）
        request->target_y = rand()%15;
        RCLCPP_ERROR(this->get_logger(),"准备好目标点%f,%f",request->target_x,request->target_y);
        
        //3.发送请求
        patrol_client_->async_send_request(
            request,
            //如果后面确定要用到request，那么[&] 改成  [this, request] 捕获：除了 this 指针，还通过值捕获 request 对象。由于 request 是 std::shared_ptr 类型，它会自动管理对象的生命周期，保证在 Lambda 表达式执行期间 request 对象不会被销毁。
            [&](rclcpp::Client<Patrol>::SharedFuture future)->void{  //[&] 表示捕获所有外部引用，这里捕获了外部的request对象和this(但是需要注意request 不会提前在外部销毁)
                //4.处理响应
                auto response = future.get(); //因为发送的是异步请求，所以不会马上返回，所以这里是futur e.get()
                if(response->result == Patrol::Response::SUCCESS){
                  RCLCPP_ERROR(this->get_logger(),"目标点到达");
                  // 注意：下面的写法是错误的，因为虽然 [&] 捕获了外部的request对象，但是request对象是在lambda表达式中被定义的，
                  //在 future.get()的时候，外部的request对象已经被销毁了，所以这里的request对象是无效的。要获取target_x和target_y，需要使用response对象。这也叫 悬空引用风险（也就是生命周期管理问题）
                  //为了避免生命周期管理问题，建议只捕获需要的变量，并且尽量使用值捕获而非引用捕获
                  //RCLCPP_ERROR(this->get_logger(),"目标点%f,%f 到达",request->target_x,request->target_y);
                }else{
                  RCLCPP_ERROR(this->get_logger(),"目标点超出范围");
                }
            }
        
        );
      }
    );    
  }


  /**
   * 创建客户端，发送请求，响应结果
   *  */ 
  SetP::Response::SharedPtr call_set_param(const rcl_interfaces::msg::Parameter& param){ //const rcl_interfaces::msg::Parameter& param 表示一个常量引用，当函数体修改了param参数，不会修改外部的param参数。
        //创建客户端
        auto param_client = this->create_client<SetP>("/turtle_control/set_parameters");
    
        //1.检测服务端是否上线
        //如果服务没有上线 就返回false 否这就返回true，也就是说 只要service没有上线，程序就会在这里阻塞住
        while (param_client->wait_for_service(1s) == false)  
        { 
            if(rclcpp::ok() == false){
                RCLCPP_ERROR(this->get_logger(),"在等待服务上线的时候 rclcpp挂了，程序退出");
                return nullptr;  //如果函数体 返回的是一个指针，那么这里就需要返回一个nullptr，而不能直接return;
            }
            RCLCPP_ERROR(this->get_logger(),"等待服务上线中...");
        }
        RCLCPP_ERROR(this->get_logger(),"服务连接成功...");

        //2.构造请求的对象
        auto request = std::make_shared<SetP::Request>();
        request->parameters.push_back(param);

        //3.发送请求
        auto future = param_client->async_send_request(request);
        rclcpp::spin_until_future_complete(this->get_node_base_interface(), future);  //异步的阻塞程序
        //4.处理响应
        auto response = future.get(); //因为发送的是异步请求，所以不会马上返回，所以这里是futur e.get()
        return response;
  }
  //更新参数
  void update_server_param_k(double k){
    //1.创建参数对象
    rcl_interfaces::msg::Parameter param;
    param.name = "k";
    //2.创建参数值
    param.value.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
    param.value.double_value = k;
    //3.调用服务
    auto response = call_set_param(param);
    if(response==NULL){
      RCLCPP_ERROR(this->get_logger(),"调用服务失败");
      return;
    }
    //返回的response 是 SetParametersResult[] results 类型
    for (auto result:response->results)
    {
      if (result.successful==false)
      {
        RCLCPP_ERROR(this->get_logger(),"参数更新失败,原因:%s",result.reason.c_str());
      }else{
        RCLCPP_ERROR(this->get_logger(),"参数更新成功");
      }
    }
    
  }


};

int main(int argc, char **argv){
  rclcpp::init(argc, argv);
  auto node = std::make_shared<PatrolClient>("patrol_client");
  node->update_server_param_k(4.0);
  
  rclcpp::spin(node); //运行node 这个节点。
  rclcpp::shutdown();
  return 0;
}